function [success, tableData, errorMsg, columnConfig] = loadslreqx(filePath, configPath)
%LOADSLREQX 加载SLREQX需求集文件并提取需求数据
%
% 输入参数:
%   filePath   - SLREQX文件的完整路径
%   configPath - 配置文件路径（可选，用于读取显示设置，YML格式）
%
% 输出参数:
%   success      - 逻辑值，表示加载是否成功
%   tableData    - 需求数据的cell数组，每行根据配置的displayColumns动态生成
%   errorMsg     - 错误消息字符串（成功时为空）
%   columnConfig - 列配置信息结构体，包含 columnNames, columnWidths, enabledColumns
%
% 示例:
%   [success, data, msg, colConfig] = loadslreqx('path/to/requirements.slreqx');
%   if success
%       disp('加载成功');
%       disp(data);
%       disp(colConfig);
%   else
%       disp(['加载失败: ', msg]);
%   end

    % 初始化输出参数
    success = false;
    tableData = {};
    errorMsg = '';
    columnConfig = struct();
    
    % 输入参数验证
    if nargin < 1 || isempty(filePath)
        errorMsg = '文件路径不能为空';
        return;
    end
    
    if nargin < 2 || isempty(configPath)
        % 使用默认配置文件路径
        scriptDir = fileparts(mfilename('fullpath'));
        configPath = fullfile(scriptDir, 'requirement_management_config.yml');
    end
    
    try
        % 验证文件存在
        if ~exist(filePath, 'file')
            errorMsg = sprintf('文件不存在: %s', filePath);
            return;
        end
        
        % 验证文件扩展名
        [~, ~, ext] = fileparts(filePath);
        if ~strcmpi(ext, '.slreqx')
            errorMsg = sprintf('不支持的文件类型: %s，请选择.slreqx文件', ext);
            return;
        end
        
        % 读取配置（如果提供）
        [displayConfig, columnConfig] = getDisplayConfig(configPath);
        
        % 规范化路径，避免路径表示不同导致冲突
        try
            normalizedPath = normalizeReqxPath(filePath);
        catch
            normalizedPath = filePath;
        end
        
        % 检查并关闭已加载的同名requirement set（基于规则第2条：明确报告错误）
        try
            % 获取所有已加载的requirement sets
            loadedReqSets = slreq.find('Type', 'ReqSet');
            
            % 从路径中提取文件名（不含扩展名）
            [~, targetName, ~] = fileparts(normalizedPath);
            
            % 检查是否存在同名的requirement set
            for i = 1:length(loadedReqSets)
                reqSet_temp = loadedReqSets(i);
                if strcmp(reqSet_temp.Name, targetName)
                    % 发现同名requirement set
                    fprintf('⚠️  发现同名 Requirement Set 已加载: %s\n', reqSet_temp.Name);
                    
                    % 获取已加载的路径（使用正确的属性名：Filename）
                    try
                        loadedPath = char(reqSet_temp.Filename);
                        loadedPathNormalized = normalizeReqxPath(loadedPath);
                        fprintf('   已加载路径: %s\n', loadedPath);
                        fprintf('   规范化已加载路径: %s\n', loadedPathNormalized);
                        
                        % 比较规范化后的路径
                        if strcmpi(loadedPathNormalized, normalizedPath)
                            % 路径相同，说明是同一个文件已经加载
                            fprintf('   ℹ️  该 Requirement Set 已经加载\n');
                            
                            % 询问用户是否要关闭并重新打开
                            answer = questdlg(...
                                sprintf('Requirement Set "%s" 已经加载。\n是否关闭并重新打开？', reqSet_temp.Name), ...
                                '确认重新打开', ...
                                '是', '否', '是');  % 默认选项为"是"
                            
                            if strcmp(answer, '否')
                                % 用户选择不重新打开，使用已加载的
                                fprintf('   ✅ 使用已加载的 Requirement Set\n');
                                success = true;
                                
                                % 获取已加载的requirement set的数据
                                try
                                    reqs = reqSet_temp.find('Type', 'Requirement');
                                    
                                    if isempty(reqs)
                                        tableData = {};
                                        fprintf('需求集已打开，但未找到需求项目\n');
                                        return;
                                    end
                                    
                                    % 提取需求数据
                                    numReqs = length(reqs);
                                    numColumns = length(columnConfig.enabledColumns);
                                    tableData = cell(numReqs, numColumns);
                                    
                                    for i = 1:numReqs
                                        req = reqs(i);
                                        for colIdx = 1:numColumns
                                            property = columnConfig.enabledColumns{colIdx};
                                            tableData{i, colIdx} = extractRequirementProperty(req, property, displayConfig);
                                        end
                                    end
                                    
                                    fprintf('成功提取 %d 个需求项目的数据（使用已加载的 Requirement Set）\n', numReqs);
                                    return;
                                catch ME
                                    fprintf('⚠️  从已加载的 Requirement Set 提取数据失败: %s\n', ME.message);
                                    % 继续执行重新打开的逻辑
                                end
                            else
                                % 用户选择关闭并重新打开
                                fprintf('   正在关闭以便重新打开...\n');
                                % 继续执行后续的关闭逻辑
                            end
                        end
                    catch ME_path
                        fprintf('   ⚠️  无法获取或规范化已加载路径: %s\n', ME_path.message);
                    end
                    fprintf('   目标路径: %s\n', normalizedPath);
                    
                    % 检查是否有未保存的修改
                    if reqSet_temp.Dirty
                        fprintf('   ⚠️  警告: Requirement Set 有未保存的修改\n');
                        errorMsg = sprintf('Requirement Set "%s" 有未保存的修改，无法自动关闭。请先保存后再加载。', reqSet_temp.Name);
                        return;
                    end
                    
                    % 关闭同名的requirement set
                    fprintf('   正在关闭冲突的 Requirement Set...\n');
                    try
                        % 尝试不同的关闭方法
                        closeSuccess = false;
                        
                        % 方法1：使用文件路径关闭
                        try
                            reqSetFilename = char(reqSet_temp.Filename);
                            slreq.close(reqSetFilename);
                            fprintf('   ✅ 已成功关闭冲突的 Requirement Set（使用文件路径）\n');
                            closeSuccess = true;
                        catch ME1
                            fprintf('   ⚠️  方法1失败: %s\n', ME1.message);
                        end
                        
                        % 如果方法1失败，尝试方法2：使用Name关闭
                        if ~closeSuccess
                            try
                                slreq.close(reqSet_temp.Name);
                                fprintf('   ✅ 已成功关闭冲突的 Requirement Set（使用名称）\n');
                                closeSuccess = true;
                            catch ME2
                                fprintf('   ⚠️  方法2失败: %s\n', ME2.message);
                            end
                        end
                        
                        % 验证是否真正关闭
                        pause(0.1);  % 等待一小段时间让MATLAB完全释放资源
                        
                        % 检查是否仍然加载
                        stillLoaded = false;
                        try
                            checkLoadedSets = slreq.find('Type', 'ReqSet');
                            for k = 1:length(checkLoadedSets)
                                if strcmp(checkLoadedSets(k).Name, targetName)
                                    stillLoaded = true;
                                    fprintf('   ⚠️  警告: Requirement Set 似乎仍然加载\n');
                                    break;
                                end
                            end
                        catch
                            % 检查失败，假设已关闭
                        end
                        
                        % 如果仍然加载，使用方法3：强制清除所有
                        if stillLoaded
                            fprintf('   正在尝试强制清除所有 Requirement Sets...\n');
                            try
                                % 先保存当前所有其他的requirement sets信息
                                allLoadedSets = slreq.find('Type', 'ReqSet');
                                otherSets = {};
                                for k = 1:length(allLoadedSets)
                                    if ~strcmp(allLoadedSets(k).Name, targetName)
                                        otherSets{end+1} = char(allLoadedSets(k).Filename);
                                    end
                                end
                                
                                % 清除所有requirement sets
                                slreq.clear();
                                fprintf('   已清除所有 Requirement Sets\n');
                                pause(0.2);  % 等待清除完成
                                
                                % 重新加载其他的requirement sets
                                for k = 1:length(otherSets)
                                    try
                                        slreq.load(otherSets{k});
                                    catch
                                        % 忽略重新加载失败的情况
                                    end
                                end
                                
                                fprintf('   ✅ 已通过清除所有集合的方式关闭冲突\n');
                                closeSuccess = true;
                            catch ME3
                                fprintf('   ❌ 方法3也失败: %s\n', ME3.message);
                            end
                        end
                        
                        if ~closeSuccess
                            error('所有关闭方法都失败');
                        end
                        
                    catch ME_close
                        fprintf('   ❌ 关闭失败: %s\n', ME_close.message);
                        errorMsg = sprintf('无法关闭冲突的 Requirement Set "%s": %s\n请手动在MATLAB中执行 slreq.clear() 后重试。', reqSet_temp.Name, ME_close.message);
                        return;
                    end
                    break;
                end
            end
        catch ME
            % 检查失败不影响加载流程，只记录警告  
            fprintf('⚠️  检查已加载 Requirement Sets 时出错: %s\n', ME.message);
        end
        
        % 加载需求集文件 (使用R2023b兼容的API)
        fprintf('正在加载需求集文件: %s\n', normalizedPath);
        reqSet = slreq.load(normalizedPath);
        
        if isempty(reqSet)
            errorMsg = '无法打开需求集文件，请检查文件格式';
            return;
        end
        
        % 获取所有需求项目 (MATLAB R2023b官方API)
        try
            % 使用find方法获取所有需求类型的对象
            reqs = reqSet.find('Type', 'Requirement');
        catch ME
            errorMsg = sprintf('无法获取需求项目: %s', ME.message);
            fprintf('%s\n', errorMsg);
            return;
        end
        
        if isempty(reqs)
            % 文件中没有需求，返回空表格但标记为成功
            tableData = {};
            success = true;
            fprintf('需求集文件已打开，但未找到需求项目\n');
            return;
        end
        
        % 提取需求数据 - 动态列数
        numReqs = length(reqs);
        numColumns = length(columnConfig.enabledColumns);
        tableData = cell(numReqs, numColumns);
        
        fprintf('正在提取 %d 个需求项目的数据（%d列）...\n', numReqs, numColumns);
        
        for i = 1:numReqs
            req = reqs(i);
            
            % 根据配置的列顺序动态提取数据
            for colIdx = 1:numColumns
                property = columnConfig.enabledColumns{colIdx};
                tableData{i, colIdx} = extractRequirementProperty(req, property, displayConfig);
            end
        end
        
        success = true;
        fprintf('成功提取 %d 个需求项目的数据\n', numReqs);
        
    catch ME
        % 捕获所有错误并返回错误消息
        errorMsg = sprintf('加载SLREQX文件时出错: %s', ME.message);
        fprintf('错误详情: %s\n', ME.message);
        if ~isempty(ME.stack)
            fprintf('错误位置: %s (行 %d)\n', ME.stack(1).name, ME.stack(1).line);
        end
        success = false;
    end
end

function [displayConfig, columnConfig] = getDisplayConfig(configPath)
%GETDISPLAYCONFIG 获取显示配置（从YML文件读取）
%
% 输出参数:
%   displayConfig - 显示相关配置（maxSummaryLength, noTitleText等，以及customAttributes）
%   columnConfig  - 列配置信息（columnNames, columnWidths, enabledColumns）
    
    % 默认显示配置
    displayConfig = struct();
    displayConfig.maxSummaryLength = 50;
    displayConfig.noTitleText = '无标题';
    displayConfig.unknownStatusText = '未知';
    displayConfig.customAttributes = {};  % 存储自定义属性列表
    
    % 默认列配置（7列全部启用）
    columnConfig = struct();
    columnConfig.columnNames = {'需求ID', '摘要', '状态', '优先级', '类型', '验证方法', '创建日期'};
    columnConfig.columnWidths = {80, 250, 80, 80, 100, 100, 100};
    columnConfig.enabledColumns = {'Id', 'Summary', 'Status', 'Priority', 'Type', 'Verify', 'CreatedOn'};
    
    % 如果提供了配置文件路径，尝试读取YML配置
    if ~isempty(configPath) && exist(configPath, 'file')
        try
            
            % 读取YML配置
            ymlConfig = yamlread(configPath);
            
            % 读取attributes配置，提取自定义属性列表
            if isfield(ymlConfig, 'attributes')
                displayConfig.customAttributes = extractCustomAttributes(ymlConfig.attributes);
            end
            
            if isfield(ymlConfig, 'slreqxSettings')
                slreqxSettings = ymlConfig.slreqxSettings;
                
                % 读取显示设置
                if isfield(slreqxSettings, 'maxSummaryLength')
                    displayConfig.maxSummaryLength = slreqxSettings.maxSummaryLength;
                end
                
                if isfield(slreqxSettings, 'noTitleText')
                    displayConfig.noTitleText = slreqxSettings.noTitleText;
                end
                
                if isfield(slreqxSettings, 'unknownStatusText')
                    displayConfig.unknownStatusText = slreqxSettings.unknownStatusText;
                end
                
                % 读取列配置
                if isfield(slreqxSettings, 'displayColumns')
                    columnConfig = parseColumnConfig(slreqxSettings.displayColumns);
                end
            end
            
            fprintf('✅ 成功加载YML配置: %s\n', configPath);
        catch ME
            % 配置文件读取失败，使用默认配置
            fprintf('⚠️ YML配置文件读取失败，使用默认显示设置: %s\n', ME.message);
        end
    end
end

function customAttrs = extractCustomAttributes(attributes)
%EXTRACTCUSTOMATTRIBUTES 从配置中提取自定义属性列表
%
% 输入参数:
%   attributes - YML配置中的attributes结构体
%
% 输出参数:
%   customAttrs - 自定义属性的targetProperty列表（cell array）
%
% 识别规则：
%   type为'combobox'的属性被视为自定义属性
%   或者targetProperty不是标准属性的被视为自定义属性

    customAttrs = {};
    
    % 标准属性列表（这些是slreq.Requirement对象的内置属性）
    standardProperties = {'Id', 'Summary', 'Description', 'Status', 'Type', ...
                         'CreatedBy', 'CreatedOn', 'ModifiedOn', 'Keywords', ...
                         'Rationale', 'Comments'};
    
    % 遍历所有属性定义
    attributeNames = fieldnames(attributes);
    for i = 1:length(attributeNames)
        attrName = attributeNames{i};
        attrConfig = attributes.(attrName);
        
        % 跳过skip类型
        if isfield(attrConfig, 'type') && strcmp(attrConfig.type, 'skip')
            continue;
        end
        
        % 检查targetProperty
        if isfield(attrConfig, 'targetProperty') && ~isempty(attrConfig.targetProperty)
            targetProp = attrConfig.targetProperty;
            
            % 如果不是标准属性，则认为是自定义属性
            if ~ismember(targetProp, standardProperties)
                customAttrs{end+1} = targetProp;
            end
        end
    end
    
    fprintf('📋 识别到%d个自定义属性: %s\n', length(customAttrs), strjoin(customAttrs, ', '));
end

function columnConfig = parseColumnConfig(displayColumns)
%PARSECOLUMNCONFIG 解析displayColumns配置
%
% 输入参数:
%   displayColumns - YML配置中的displayColumns结构体
%
% 输出参数:
%   columnConfig - 解析后的列配置结构体
    
    columnConfig = struct();
    
    % 获取所有列的配置
    columnFields = fieldnames(displayColumns);
    numFields = length(columnFields);
    
    % 预分配数组
    columnData = cell(numFields, 5);  % [fieldName, displayName, property, width, order]
    
    for i = 1:numFields
        fieldName = columnFields{i};
        colConfig = displayColumns.(fieldName);
        
        % 只处理启用的列
        if isfield(colConfig, 'enabled') && colConfig.enabled
            order = 999;  % 默认顺序
            if isfield(colConfig, 'order')
                order = colConfig.order;
            end
            
            columnData{i, 1} = fieldName;
            columnData{i, 2} = colConfig.displayName;
            columnData{i, 3} = colConfig.property;
            columnData{i, 4} = colConfig.width;
            columnData{i, 5} = order;
        end
    end
    
    % 移除未启用的列（空行）
    validRows = ~cellfun(@isempty, columnData(:, 1));
    columnData = columnData(validRows, :);
    
    % 按order排序
    if ~isempty(columnData)
        orders = cell2mat(columnData(:, 5));
        [~, sortIdx] = sort(orders);
        columnData = columnData(sortIdx, :);
        
        % 提取配置信息
        columnConfig.enabledColumns = columnData(:, 3)';  % property names
        columnConfig.columnNames = columnData(:, 2)';     % display names
        columnConfig.columnWidths = columnData(:, 4)';    % widths
    else
        % 没有启用的列，使用默认配置
        columnConfig.columnNames = {'需求ID', '摘要', '状态', '优先级', '类型', '验证方法', '创建日期'};
        columnConfig.columnWidths = {80, 250, 80, 80, 100, 100, 100};
        columnConfig.enabledColumns = {'Id', 'Summary', 'Status', 'Priority', 'Type', 'Verify', 'CreatedOn'};
    end
end

function value = extractRequirementProperty(req, property, displayConfig)
%EXTRACTREQUIREMENTPROPERTY 从需求对象中提取指定属性的值
%
% 输入参数:
%   req           - 需求对象
%   property      - 属性名称
%   displayConfig - 显示配置
%
% 输出参数:
%   value - 属性值（字符串）
    
    value = '';
    
    switch property
        case 'Id'
            if isprop(req, 'Id') && ~isempty(req.Id)
                value = char(req.Id);
            else
                value = sprintf('REQ_%03d', 1);  % 默认ID
            end
            
        case 'Summary'
            if isprop(req, 'Summary') && ~isempty(req.Summary)
                summary = char(req.Summary);
                value = truncateText(summary, displayConfig.maxSummaryLength);
            elseif isprop(req, 'Description') && ~isempty(req.Description)
                desc = char(req.Description);
                value = truncateText(desc, displayConfig.maxSummaryLength);
            else
                value = displayConfig.noTitleText;
            end
            
        case 'Status'
            if isprop(req, 'Status') && ~isempty(req.Status)
                value = char(req.Status);
            else
                value = displayConfig.unknownStatusText;
            end
            
        case 'Priority'
            if isprop(req, 'Priority') && ~isempty(req.Priority)
                value = char(req.Priority);
            else
                value = '中';  % 默认优先级
            end
            
        case 'Type'
            if isprop(req, 'Type') && ~isempty(req.Type)
                reqType = char(req.Type);
                value = mapRequirementType(reqType);
            else
                value = '功能需求';  % 默认类型
            end
            
        case 'Verify'
            % 尝试多个可能的属性名
            if isprop(req, 'Verify') && ~isempty(req.Verify)
                value = char(req.Verify);
            elseif isprop(req, 'VerificationMethod') && ~isempty(req.VerificationMethod)
                value = char(req.VerificationMethod);
            elseif isprop(req, 'Verification') && ~isempty(req.Verification)
                value = char(req.Verification);
            else
                value = '测试';  % 默认验证方法
            end
            
        case 'CreatedOn'
            if isprop(req, 'CreatedOn') && ~isempty(req.CreatedOn)
                try
                    value = datestr(req.CreatedOn, 'yyyy-mm-dd');
                catch
                    value = datestr(now, 'yyyy-mm-dd');
                end
            elseif isprop(req, 'ModifiedOn') && ~isempty(req.ModifiedOn)
                try
                    value = datestr(req.ModifiedOn, 'yyyy-mm-dd');
                catch
                    value = datestr(now, 'yyyy-mm-dd');
                end
            else
                value = datestr(now, 'yyyy-mm-dd');
            end
            
        otherwise
            % 检查是否为自定义属性（从配置中识别）
            if isfield(displayConfig, 'customAttributes') && ...
               ismember(property, displayConfig.customAttributes)
                % 这是自定义属性，尝试使用getAttribute读取
                try
                    attrValue = getAttribute(req, property);
                    if ~isempty(attrValue)
                        value = char(attrValue);
                    else
                        value = '';
                    end
                catch
                    value = '';  % 如果属性不存在，返回空字符串
                end
            else
                % 未知属性，尝试直接读取
                if isprop(req, property) && ~isempty(req.(property))
                    value = char(req.(property));
                else
                    value = '';
                end
            end
    end
end

function truncatedText = truncateText(text, maxLength)
%TRUNCATETEXT 截断文本到指定长度
    if length(text) > maxLength
        truncatedText = [text(1:maxLength-3), '...'];
    else
        truncatedText = text;
    end
end

function mappedType = mapRequirementType(originalType)
%MAPREQUIREMENTTYPE 将SLREQX的原始类型映射到应用程序的类型选项
%
% 输入参数:
%   originalType - SLREQX文件中的原始类型字符串
%
% 输出参数:
%   mappedType - 映射后的类型字符串，对应应用程序的类型选项
%
% 支持的映射：
%   Functional -> 功能需求
%   Container -> 文档  
%   Informational -> 文档
%   Safety -> 安全需求
%   Performance -> 性能需求

    % 将输入转换为小写以进行大小写不敏感的匹配
    lowerType = lower(char(originalType));
    
    switch lowerType
        case {'functional', 'function', 'func'}
            mappedType = '功能需求';
        case {'safety', 'safe'}
            mappedType = '安全需求';
        case {'performance', 'perf'}
            mappedType = '性能需求';
        case {'container', 'informational', 'info', 'document', 'doc'}
            mappedType = '文档';
        otherwise
            % 对于未知类型，默认映射为功能需求
            mappedType = '功能需求';
            fprintf('警告: 未知的需求类型 "%s"，映射为默认类型 "功能需求"\n', originalType);
    end
end
